
#include "app_camer.h"

#include "../public/public_msg/public_msg.h"
//********************************************************************************
#define TRUE            (1)
#define FALSE           (0)

#define FILE_VIDEO      "/dev/video0"
#define IMAGE           "./image/demo"

#define IMAGEWIDTH      1024
#define IMAGEHEIGHT     600

#define FRAME_NUM       4

int fd;
struct v4l2_buffer buf;

//https://www.2cto.com/kf/201612/575921.html    ov2655驱动分析
//https://blog.csdn.net/weixin_42462202/article/details/99765607
//https://blog.csdn.net/liukun321/article/details/38376109


struct buffer
{
    void * start;
    unsigned int length;
    long long int timestamp;
} *buffers;


int v4l2_init()
{
    int argp = 0;
	struct v4l2_capability cap;
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_format fmt;
    struct v4l2_streamparm stream_para;

    //打开摄像头设备
    if ((fd = open(FILE_VIDEO, O_RDWR)) == -1) 
    {
        debug_d("Error opening V4L interface\n");
        return FALSE;
    }
	debug_d("0=================================================\n");
	argp = 0;
	if (ioctl(fd, VIDIOC_S_INPUT, &argp) == -1) 
    {
        debug_d("Error VIDIOC_S_INPUT\n");
        return FALSE;
    }
	else
	{     
		debug_d("argp=%d\n",argp);
		
	}
	debug_d("00=================================================\n");
    if (ioctl(fd, VIDIOC_G_INPUT, &argp) == -1) 
    {
        debug_d("Error VIDIOC_G_INPUT\n");
        return FALSE;
    }
	else
	{     
		debug_d("argp=%d\n",argp);
		
	}
	debug_d("1=================================================\n");
    //查询设备属性
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) 
    {
        debug_d("Error opening device %s: unable to query device.\n",FILE_VIDEO);
        return FALSE;
    }
    else
    {
        debug_d("driver:\t\t%s\n",cap.driver);
        debug_d("card:\t\t%s\n",cap.card);
        debug_d("bus_info:\t%s\n",cap.bus_info);
        debug_d("version:\t%d\n",cap.version);
        debug_d("capabilities:\t%x\n",cap.capabilities);
        
        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) 
        {
            debug_d("Device %s: supports capture.\n",FILE_VIDEO);
        }

        if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) 
        {
            debug_d("Device %s: supports streaming.\n",FILE_VIDEO);
        }
    }
	debug_d("2=================================================\n");

    //显示所有支持帧格式
    fmtdesc.index=0;
    fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    debug_d("Support format:\n");
    while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    {
        debug_d("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
        fmtdesc.index++;
		if(fmtdesc.index > 20)
		{
			break;
		}
    }
	debug_d("3=================================================\n");
    //检查是否支持某帧格式
    struct v4l2_format fmt_test;
    fmt_test.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt_test.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;
    if(ioctl(fd,VIDIOC_TRY_FMT,&fmt_test)==-1)
    {
        debug_d("not support format RGB32!\n");      
    }
    else
    {
        debug_d("support format RGB32\n");
    }
	
	debug_d("4=================================================\n");

    //查看及设置当前格式
    debug_d("set fmt...\n");
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; //jpg格式
    //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//yuv格式
	//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; //jpg格式


    fmt.fmt.pix.height = IMAGEHEIGHT;
    fmt.fmt.pix.width = IMAGEWIDTH;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;//V4L2_FIELD_ANY;V4L2_FIELD_INTERLACED;
    debug_d("fmt.type:\t\t%d\n",fmt.type);
    debug_d("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
    debug_d("pix.height:\t\t%d\n",fmt.fmt.pix.height);
    debug_d("pix.width:\t\t%d\n",fmt.fmt.pix.width);
    debug_d("pix.field:\t\t%d\n",fmt.fmt.pix.field);
    if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    {
        debug_d("Unable to set format\n");
        return FALSE;
    }

    debug_d("get fmt...\n"); 
    if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    {
        debug_d("Unable to get format\n");
        return FALSE;
    }
    {
        debug_d("fmt.type:\t\t%d\n",fmt.type);
        debug_d("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
        debug_d("pix.height:\t\t%d\n",fmt.fmt.pix.height);
        debug_d("pix.width:\t\t%d\n",fmt.fmt.pix.width);
        debug_d("pix.field:\t\t%d\n",fmt.fmt.pix.field);
    }

    //设置及查看帧速率，这里只能是30帧，就是1秒采集30张图
    memset(&stream_para, 0, sizeof(struct v4l2_streamparm));
    stream_para.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    stream_para.parm.capture.timeperframe.denominator = 30;
    stream_para.parm.capture.timeperframe.numerator = 1;

    if(ioctl(fd, VIDIOC_S_PARM, &stream_para) == -1)
    {
        debug_d("Unable to set frame rate\n");
        return FALSE;
    }
    if(ioctl(fd, VIDIOC_G_PARM, &stream_para) == -1)
    {
        debug_d("Unable to get frame rate\n");
        return FALSE;       
    }
    {
        debug_d("numerator:%d\ndenominator:%d\n",stream_para.parm.capture.timeperframe.numerator,stream_para.parm.capture.timeperframe.denominator);
    }
    return TRUE;
}



int v4l2_mem_ops()
{
    unsigned int n_buffers;
    struct v4l2_requestbuffers req;
    
    //申请帧缓冲
    req.count=FRAME_NUM;
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory=V4L2_MEMORY_MMAP;   
    if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
    {
        debug_d("request for buffers error\n");
        return FALSE;
    }

    // 申请用户空间的地址列
    buffers = malloc(req.count*sizeof (*buffers));
    if (!buffers) 
    {
        debug_d ("out of memory!\n");
        return FALSE;
    }
    
    // 进行内存映射
    for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++) 
    {
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = n_buffers;
        //查询
        if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1)
        {
            debug_d("query buffer error\n");
            return FALSE;
        }

        //映射
        buffers[n_buffers].length = buf.length;
        buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
        if (buffers[n_buffers].start == MAP_FAILED)
        {
            debug_d("buffer map error\n");
            return FALSE;
        }
    }
    return TRUE;    
}



int v4l2_frame_process()
{
    unsigned int n_buffers;
    enum v4l2_buf_type type;
    char file_name[100];
    char index_str[10];
    long long int extra_time = 0;
    long long int cur_time = 0;
    long long int last_time = 0;

	debug_d("\n");
	
    //入队和开启采集
    for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
    {
        buf.index = n_buffers;
		buf.memory = V4L2_MEMORY_MMAP;
        ioctl(fd, VIDIOC_QBUF, &buf);
    }

	debug_d("\n");
		
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd, VIDIOC_STREAMON, &type);
    

	debug_d("\n");

    //出队，处理，写入yuv文件，入队，循环进行
    int loop = 0;
    while(loop < 10000)
    {
        for(n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
        {

			//出队
            buf.index = n_buffers;
			buf.memory = V4L2_MEMORY_MMAP;
            ioctl(fd, VIDIOC_DQBUF, &buf);

            //查看采集数据的时间戳之差，单位为微妙
            buffers[n_buffers].timestamp = buf.timestamp.tv_sec*1000000+buf.timestamp.tv_usec;
            cur_time = buffers[n_buffers].timestamp;
            extra_time = cur_time - last_time;
            last_time = cur_time;
            //debug_d("time_deta:%lld\n\n",extra_time);
            //debug_d("buf_len:%d\n",buffers[n_buffers].length);

            //处理数据只是简单写入文件，名字以loop的次数和帧缓冲数目有关
            //debug_d("grab image data OK\n");
            memset(file_name,0,sizeof(file_name));
            memset(index_str,0,sizeof(index_str));
            sprintf(index_str,"%d",loop*4+n_buffers);
            strcpy(file_name,IMAGE);
            strcat(file_name,index_str);
            strcat(file_name,".jpg");
            //strcat(file_name,".yuv");
            #if 0
            FILE *fp2 = fopen(file_name, "wb");
            if(!fp2)
            {
                debug_d("open %s error\n",file_name);
                return(FALSE);
            }
            fwrite(buffers[n_buffers].start, IMAGEHEIGHT*IMAGEWIDTH*2,1,fp2);
            fclose(fp2);
            debug_d("save %s OK\n",file_name);
			#endif

            //入队循环

			lib_drm_hdmi_yuv_show(buffers[n_buffers].start,800,600,800*600*2);

            ioctl(fd, VIDIOC_QBUF, &buf);       
        }

        loop++;
    }
    return TRUE;    
}




int v4l2_release()
{
    unsigned int n_buffers;
    enum v4l2_buf_type type;

    //关闭流
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd, VIDIOC_STREAMON, &type);
    
    //关闭内存映射
    for(n_buffers=0;n_buffers<FRAME_NUM;n_buffers++)
    {
        munmap(buffers[n_buffers].start,buffers[n_buffers].length);
    }
    
    //释放自己申请的内存
    free(buffers);
    
    //关闭设备
    close(fd);
    return TRUE;
}


int start_captuer(void)
{
    debug_d("begin....\n");
    sleep(1);

    v4l2_init();
    debug_d("init....\n");
    sleep(1);

    v4l2_mem_ops();
    debug_d("malloc....\n");
    sleep(1);

    v4l2_frame_process();
    debug_d("process....\n");
    sleep(1);

    v4l2_release();
    debug_d("release\n");
    sleep(1);
    
    return TRUE;
}
//********************************************************************************


int32_t app_camer_create(void)
{
	
	int  ret  = -1;

	//********************************************************************************
	log_i(" camer app create\n");
	//********************************************************************************

	start_captuer();
	//********************************************************************************

	return 0;
}

int32_t app_camer_destory(void)
{

	log_i("camer exit end\n");
		

	return 0;
}
